﻿using System;
using Assignment_2.Entities.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Assignment_2.Entities
{
    class World : Entity
    {
        public Texture2D Heightmap { get; set; }
        public Texture2D WorldTex { get; set; }

        private Color[,] _colors;

        private const float Radius = 100.0f;

        public struct HSL
        {
            public double h, s, l;
        }

        public World(GraphicsDevice device, Texture2D heightmap, Texture2D worldTex)
        {
            Heightmap = heightmap;
            WorldTex = worldTex;

            Initialize(device);
        }

        public void Initialize(GraphicsDevice device)
        {
            _colors = TextureTo2DArray(Heightmap);

            DrawComponents.Add("WorldMesh", new VptComponent(this, device, InitializeVertices(), InitializeIndices()) { Texture = WorldTex});
        }

        private VertexPositionTexture[] InitializeVertices()
        {
            var vertices = new VertexPositionTexture[Heightmap.Width * Heightmap.Height + Heightmap.Height];

            for (var x = 0; x < Heightmap.Width; x++)
            {
                for (var y = 0; y < Heightmap.Height; y++)
                {
                    vertices[x + y * Heightmap.Width] = new VertexPositionTexture(ConvertToSphere(new Vector3(x, y, 0)), new Vector2(0, 0));
                }
            }


            for (var y = 0; y < Heightmap.Height; y++)
            {
                vertices[Heightmap.Width + 1 + y * Heightmap.Width] = vertices[0 + y * Heightmap.Width];
                vertices[Heightmap.Width + 1 + y * Heightmap.Width].TextureCoordinate = new Vector2((float)(Heightmap.Width + 1) / (Heightmap.Width), (float)(y) / (Heightmap.Height));
            }

            for (var x = 0; x < Heightmap.Width + 1; x++)
            {
                for (var y = 0; y < Heightmap.Height; y++)
                {
                    vertices[x + y * Heightmap.Width].TextureCoordinate = new Vector2((float)(x) / (Heightmap.Width), (float)(y) / (Heightmap.Height));
                }
            }

            return vertices;
        }

        public Vector3 ConvertToSphere(Vector3 cords)
        {
            var height = RgbToHsl(_colors[(int)cords.X, (int)cords.Y]);
            var ringradius = (float)(Radius * Math.Sin(-cords.Y * Math.PI / Heightmap.Height)) + ((float)height.h / 50) + cords.Z;

            return new Vector3(
                                        (float)(Math.Cos((Heightmap.Width - cords.X) * Math.PI * 2.0f / Heightmap.Width) * ringradius),
                                        (float)(Math.Cos(cords.Y * Math.PI / Heightmap.Height) * Radius),
                                        (float)(Math.Sin((Heightmap.Width - cords.X) * Math.PI * 2.0f / Heightmap.Width) * ringradius));
        }

        private int[] InitializeIndices()
        {
            var indices = new int[6 * (Heightmap.Width - 1) * (Heightmap.Height - 1) + (Heightmap.Height - 1) * 6];
            var number = 0;

            for (var y = 0; y < Heightmap.Height - 1; y++)
            {
                for (var x = 0; x < Heightmap.Width; x++)
                {
                    indices[number] = x + (y + 1) * Heightmap.Width;      // up left
                    indices[number + 1] = x + y * Heightmap.Width;            // down left
                    indices[number + 2] = x + y * Heightmap.Width + 1;        // down right
                    indices[number + 3] = x + (y + 1) * Heightmap.Width;      // up left
                    indices[number + 4] = x + y * Heightmap.Width + 1;        // down right
                    indices[number + 5] = x + (y + 1) * Heightmap.Width + 1;  // up right
                    number += 6;
                }
            }

            return indices;
        }

        private static Color[,] TextureTo2DArray(Texture2D texture)
        {
            var colors1D = new Color[texture.Width * texture.Height];
            texture.GetData(colors1D);

            var colors2D = new Color[texture.Width, texture.Height];
            for (int x = 0; x < texture.Width; x++)
                for (int y = 0; y < texture.Height; y++)
                    colors2D[x, y] = colors1D[x + y * texture.Width];

            return colors2D;
        }

        private static HSL RgbToHsl(Color c1)
        {
            HSL c2;
            double themin = Math.Min(c1.R, Math.Min(c1.G, c1.B));
            double themax = Math.Max(c1.R, Math.Max(c1.G, c1.B));
            var delta = themax - themin;

            c2.l = (themin + themax) / 2;
            c2.s = 0;
            if (c2.l > 0 && c2.l < 1)
                c2.s = delta / (c2.l < 0.5 ? (2 * c2.l) : (2 - 2 * c2.l));
            c2.h = 0;
            if (delta > 0)
            {
                if (themax == c1.R && themax != c1.G)
                    c2.h += (c1.G - c1.B) / delta;
                if (themax == c1.G && themax != c1.B)
                    c2.h += (2 + (c1.B - c1.R) / delta);
                if (themax == c1.B && themax != c1.R)
                    c2.h += (4 + (c1.R - c1.G) / delta);
                c2.h *= 60;
            }
            return (c2);
        }
    }
}
